
package com.lry.rpc.starter.util;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.SingletonBeanRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;

@Slf4j
public abstract class AnnotatedBeanDefinitionRegistryUtils {

    public AnnotatedBeanDefinitionRegistryUtils() {
    }


    /**
     * 判断annotatedClass 有没有在spring容器中
     * @param registry
     * @param annotatedClass
     * @return
     */
    public static boolean isPresentBean(BeanDefinitionRegistry registry, Class<?> annotatedClass) {
        boolean present = false;

        String[] beanNames = registry.getBeanDefinitionNames();

        ClassLoader classLoader = annotatedClass.getClassLoader();

        for (String beanName : beanNames) {
            BeanDefinition beanDefinition = registry.getBeanDefinition(beanName);
            if (beanDefinition instanceof AnnotatedBeanDefinition) {
                AnnotationMetadata annotationMetadata = ((AnnotatedBeanDefinition)beanDefinition).getMetadata();
                String className = annotationMetadata.getClassName();
                Class<?> targetClass = ClassUtils.resolveClassName(className, classLoader);
                present = ObjectUtils.nullSafeEquals(targetClass, annotatedClass);
                if (present) {
                    if (log.isDebugEnabled()) {
                        log.debug(String.format("The annotatedClass[class : %s , bean name : %s] was present in registry[%s]", className, beanName, registry));
                    }
                    break;
                }
            }
        }

        return present;
    }

    public static void registerBeans(BeanDefinitionRegistry registry, Class... annotatedClasses) {
        if(ObjectUtils.isEmpty(annotatedClasses)){
            return;
        }

        Set<Class<?>> classesToRegister = new LinkedHashSet(Arrays.asList(annotatedClasses));
        Iterator iterator = classesToRegister.iterator();

        //把已经在spring容器中的移除
        while(iterator.hasNext()) {
            Class<?> annotatedClass = (Class)iterator.next();
            if (isPresentBean(registry, annotatedClass)) {
                iterator.remove();
            }
        }

        AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(registry);
        if (log.isDebugEnabled()) {
            log.debug(registry.getClass().getSimpleName() + " will register annotated classes : " + Arrays.asList(annotatedClasses) + " .");
        }

        reader.register((Class[])classesToRegister.toArray(new Class[0]));
    }

    public static int scanBasePackages(BeanDefinitionRegistry registry, String... basePackages) {
        int count = 0;
        if (!ObjectUtils.isEmpty(basePackages)) {
            boolean debugEnabled = log.isDebugEnabled();
            if (debugEnabled) {
                log.debug(registry.getClass().getSimpleName() + " will scan base packages " + Arrays.asList(basePackages) + ".");
            }

            List<String> registeredBeanNames = Arrays.asList(registry.getBeanDefinitionNames());
            ClassPathBeanDefinitionScanner classPathBeanDefinitionScanner = new ClassPathBeanDefinitionScanner(registry);
            count = classPathBeanDefinitionScanner.scan(basePackages);
            List<String> scannedBeanNames = new ArrayList(count);
            scannedBeanNames.addAll(Arrays.asList(registry.getBeanDefinitionNames()));
            scannedBeanNames.removeAll(registeredBeanNames);
            if (debugEnabled) {
                log.debug("The Scanned Components[ count : " + count + "] under base packages " + Arrays.asList(basePackages) + " : ");
            }

            Iterator var7 = scannedBeanNames.iterator();

            while(var7.hasNext()) {
                String scannedBeanName = (String)var7.next();
                BeanDefinition scannedBeanDefinition = registry.getBeanDefinition(scannedBeanName);
                if (debugEnabled) {
                    log.debug("Component [ name : " + scannedBeanName + " , class : " + scannedBeanDefinition.getBeanClassName() + " ]");
                }
            }
        }

        return count;
    }

    public static BeanNameGenerator resolveAnnotatedBeanNameGenerator(BeanDefinitionRegistry registry) {
        BeanNameGenerator beanNameGenerator = null;
        if (registry instanceof SingletonBeanRegistry) {
            SingletonBeanRegistry singletonBeanRegistry = (SingletonBeanRegistry)SingletonBeanRegistry.class.cast(registry);
            beanNameGenerator = (BeanNameGenerator)singletonBeanRegistry.getSingleton("org.springframework.context.annotation.internalConfigurationBeanNameGenerator");
        }

        if (beanNameGenerator == null) {
            if (log.isInfoEnabled()) {
                log.info("BeanNameGenerator bean can't be found in BeanFactory with name [org.springframework.context.annotation.internalConfigurationBeanNameGenerator]");
                log.info("BeanNameGenerator will be a instance of " + AnnotationBeanNameGenerator.class.getName() + " , it maybe a potential problem on bean name generation.");
            }

            beanNameGenerator = new AnnotationBeanNameGenerator();
        }

        return (BeanNameGenerator)beanNameGenerator;
    }

    public static Set<BeanDefinitionHolder> findBeanDefinitionHolders(ClassPathBeanDefinitionScanner scanner, String packageToScan, BeanDefinitionRegistry registry, BeanNameGenerator beanNameGenerator) {
        Set<BeanDefinition> beanDefinitions = scanner.findCandidateComponents(packageToScan);
        Set<BeanDefinitionHolder> beanDefinitionHolders = new LinkedHashSet(beanDefinitions.size());
        Iterator var6 = beanDefinitions.iterator();

        while(var6.hasNext()) {
            BeanDefinition beanDefinition = (BeanDefinition)var6.next();
            String beanName = beanNameGenerator.generateBeanName(beanDefinition, registry);
            BeanDefinitionHolder beanDefinitionHolder = new BeanDefinitionHolder(beanDefinition, beanName);
            beanDefinitionHolders.add(beanDefinitionHolder);
        }

        return beanDefinitionHolders;
    }
}
